/**
 * 
 */
package registnet.courseDemand;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import registnet.dao.ClassroomView;
import registnet.dao.ClassroomViewPeer;
import registnet.dao.CourseView;
import registnet.dao.CourseViewPeer;
import registnet.dao.SectionLaboratory;
import registnet.dao.SectionLecture;
import registnet.dao.SectionView;
import registnet.dao.Slot;
import registnet.dao.StudentView;
import registnet.dao.StudentViewPeer;

/**
 * The Students Needing the Course Approach. This is the second approach for
 * Course Demand-Based Approach.
 * 
 * @author Dexter Ryan Carrasco
 */
public class StudentsNeedingAlgorithm implements CourseDemandConstants {

	/** The map of room categories and rooms */
	private Map<String, List<ClassroomView>> roomsList;

	/** The list of courses */
	private List<CourseView> coursesList;

	/** The map of student number and students */
	private Map<String, StudentView> studentsList;

	/** The list of courses with missing information */
	private final List<String> coursesWithMissingInfo;

	/** The list of courses with not yet implemented course schedule */
	private final List<String> notYetImplementedCourses;

	/** The list of course types */
	private List<Integer> courseTypesList;

	/** The list of students with missing information */
	private final List<String> studentsWithMissingStudentInfo;

	/** The map of course and number of students that did not get the course */
	private final Map<String, Integer> unscheduledStudentsMap;

	/** The map of students and the number of courses he did not get */
	private final Map<String, Integer> underloadedStudentsMap;

	/** Default Constructor for CourseDemandAppproach2 */
	public StudentsNeedingAlgorithm() {
		this.coursesWithMissingInfo = new ArrayList<String>();
		this.notYetImplementedCourses = new ArrayList<String>();
		this.studentsWithMissingStudentInfo = new ArrayList<String>();
		this.unscheduledStudentsMap = new HashMap<String, Integer>();
		this.underloadedStudentsMap = new HashMap<String, Integer>();
	}

	/**
	 * Assigns the lecture sections of a given lecture based on its schedule
	 * 
	 * @param lectureSections
	 * @return list of section names that are the same for including number e.g.
	 *         A1 , A2
	 */
	private List<String> assignSections(
			final List<SectionLecture> lectureSections) {
		List<String> sameSections = new ArrayList<String>();
		final List<String> sectionsAssigned = new ArrayList<String>();

		for (final Iterator<SectionLecture> lectIter = lectureSections
				.iterator(); lectIter.hasNext();) {
			final SectionLecture lecture = lectIter.next();
			final String section = ScheduleUtility.getSectionName(lecture
					.getTimeInt(), lecture.getDaysInt());
			lecture.setLecSectionName(section);

			if (sectionsAssigned.contains(section)
					&& !sameSections.contains(section)) {
				sameSections.add(section);
			} else {
				if (!sectionsAssigned.contains(section)
						&& !section.equals("TBA")) {
					sectionsAssigned.add(section);
				}
			}
		}

		if (sameSections.size() < 1) {
			sameSections = null;
		}

		return sameSections;
	}

	/**
	 * Finds students from the specified studentsNeeding who are vacant on the
	 * specified schedule. It limits the number of results to
	 * maxNumberOfStudents.
	 * 
	 * @param studentsNeeding
	 * @param pair
	 * @param maxNumberOfStudents
	 * @return list of student numbers
	 */
	private List<String> findStudentsVacantOn(
			final List<String> studentsNeeding, final SchedulePair pair,
			final int maxNumberOfStudents) {
		int ctr = 0;
		final List<String> vacantStudents = new ArrayList<String>();
		final List<Integer> daysInt = pair.getDaysInt();
		final int[] timeInt = pair.getTimeInt();

		for (final Iterator<String> studIter = studentsNeeding.iterator(); studIter
				.hasNext();) {
			final String studentNumber = studIter.next();
			final StudentView student = this.studentsList.get(studentNumber);

			if (this.studentsWithMissingStudentInfo.contains(studentNumber)) {
				continue;
			}

			if (student == null) {
				this.studentsWithMissingStudentInfo.add(studentNumber);

				continue;
			}

			if (student.isVacant(daysInt, timeInt)) {
				vacantStudents.add(studentNumber);

				// if max number of students is equal to 0, it means that the
				// slots are unlimited, so there is no need for ctr
				if (maxNumberOfStudents != 0) {
					ctr++;

					if (ctr == maxNumberOfStudents) {
						break;
					}
				}
			}
		}

		return vacantStudents;
	}

	/**
	 * Gets the maximum number of students in a lecture. If the course has a
	 * laboratory, the maximum number of students in a laboratory multiplied to
	 * the maximum number of laboratory per lecture is returned, else only the
	 * maximum number of lecture students are considered.
	 * 
	 * @param course
	 * @return
	 */
	private int getMaxLecStudents(final CourseView course) {
		if (course.getHasLaboratory()) {
			return course.getMaximumNumberOfLaboratoryStudents()
					* course.getMaxLabPerLec();
		} else {
			return course.getMaximumNumberOfLectureStudents();
		}
	}

	/**
	 * Gets the set of room and schedule that satisfies the given parameters
	 * 
	 * @param roomCategory
	 * @param possibleSkeds
	 * @param possibleRooms
	 * @param courseCode
	 * @param capacityNeeded
	 * @return list of possible room and schedules
	 */
	private List<RoomAndSkedPair> getRoomAndSked(final String roomCategory,
			final List<SchedulePair> possibleSkeds,
			List<ClassroomView> possibleRooms, final String courseCode,
			final int capacityNeeded) {
		List<RoomAndSkedPair> roomAndSked = new ArrayList<RoomAndSkedPair>();

		if (roomCategory != null) {
			if (possibleRooms.size() > 0) {
				roomAndSked = this.searchForRoomAndSked(possibleSkeds,
						possibleRooms, courseCode);
			}

			final int roundedCapacity = (int) Math.floor(capacityNeeded / 10) * 10;
			final List<ClassroomView> newPossibleRooms = new ArrayList<ClassroomView>();

			try {
				possibleRooms = this.roomsList.get(roomCategory);
				for (final Iterator<ClassroomView> roomIter = possibleRooms
						.iterator(); roomIter.hasNext();) {
					final ClassroomView room = roomIter.next();

					// edit the condition if you want to bound the capacity of
					// rooms
					if (room.getCapacity() >= roundedCapacity
							&& room.getCapacity() <= 2 * capacityNeeded) {
						newPossibleRooms.add(room);
					}
				}
			} catch (final NullPointerException ne) {
				System.out.println("roomCategory " + roomCategory);
			}

			if (newPossibleRooms.size() > 0) {
				CourseDemandUtility.sortClassroomsByCapacity(newPossibleRooms);
				try {
					roomAndSked.addAll(this.searchForRoomAndSked(possibleSkeds,
							newPossibleRooms, courseCode));
				} catch (final NullPointerException ne) {
					System.out.println(roomCategory + courseCode);
				}
			}

		} else {
			// System.out.println("TODO: null roomCategory");
			// roomAndSked = new RoomAndSkedPair(null, possibleSkeds.get(0));
		}

		return roomAndSked;
	}

	/**
	 * Initializes course types. You can add course types according to priority.
	 */
	private void initCourseTypesList() {
		this.courseTypesList = new ArrayList<Integer>();

		this.courseTypesList.add(MAJOR_COURSES);
		this.courseTypesList.add(GE_COURSES);
		this.courseTypesList.add(PE_COURSES);
		this.courseTypesList.add(NSTP_COURSES);

		System.out.println(this.courseTypesList.size()
				+ " course types have been initialized");
	}

	/** Initializes rooms */
	private void initRoomsList() {
		this.roomsList = ClassroomViewPeer.getCategorizedRooms();
		System.out.println(this.roomsList.size()
				+ " Room Categories have been initialized");
	}

	/** Initializes students */
	private void initStudentsList() {
		this.studentsList = StudentViewPeer.getAllStudents();
		System.out.println(this.studentsList.size()
				+ " Students have been initialized");
	}

	/**
	 * Prints the summary of the program
	 */
	private void printSummary() {
		this.printUnderloadedStudents();
		this.printUnscheduledStudents();
		for (final Iterator<String> stud = this.studentsWithMissingStudentInfo
				.iterator(); stud.hasNext();) {
			System.out.println(stud.next());
		}
	}

	/**
	 * Prints to SNA_underloaded_students.csv file the underloaded students and
	 * their unscheduled courses
	 */
	private void printUnderloadedStudents() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SNA_underloaded_students_" + datetime
				+ ".csv");
		final Set<String> students = this.underloadedStudentsMap.keySet();

		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF UNDERLOADED STUDENTS ON STUDENTS NEEDING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output.write("STUDENT NO.,LACK UNITS,UNSCHEDULED COURSES");
			output.newLine();

			for (final Iterator<String> studIter = students.iterator(); studIter
					.hasNext();) {
				final String student = studIter.next();
				final StudentView studentObj = this.studentsList.get(student);
				final Set<String> unscheduledCourses = studentObj
						.getUnscheduledCourses();
				final StringBuffer unscheduledCoursesString = new StringBuffer();

				for (final Iterator<String> courseIter = unscheduledCourses
						.iterator(); courseIter.hasNext();) {
					final String course = courseIter.next();

					unscheduledCoursesString.append(course);
					unscheduledCoursesString.append(" + ");
				}

				output.write(student + "," + studentObj.getLackUnits() + ","
						+ unscheduledCoursesString);
				output.newLine();
			}

			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Prints to SNA_unscheduled_per_course.csv file the number of unscheduled
	 * students per course
	 */
	private void printUnscheduledStudents() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SNA_unscheduled_per_course_" + datetime
				+ ".csv");
		final Set<String> coursecodes = this.unscheduledStudentsMap.keySet();

		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF UNSCHEDULED STUDENTS PER COURSE ON STUDENTS NEEDING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output.write("COURSE,UNSCHEDULED STUDENTS");
			output.newLine();

			for (final Iterator<String> courseIter = coursecodes.iterator(); courseIter
					.hasNext();) {
				final String course = courseIter.next();
				final int unscheduled = this.unscheduledStudentsMap.get(course);

				output.write(course + "," + unscheduled);
				output.newLine();
			}

			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Search for a matching pair of room and schedule e.g. room that is vacant
	 * in a given schedule.
	 * 
	 * @param possibleSkeds
	 * @param possibleRooms
	 * @param courseCode
	 * @return list of matching room and schedule
	 */
	private List<RoomAndSkedPair> searchForRoomAndSked(
			final List<SchedulePair> possibleSkeds,
			final List<ClassroomView> possibleRooms, final String courseCode) {
		final List<RoomAndSkedPair> roomAndSked = new ArrayList<RoomAndSkedPair>();

		for (final Iterator<SchedulePair> pairIter = possibleSkeds.iterator(); pairIter
				.hasNext();) {
			final SchedulePair pair = pairIter.next();

			for (final Iterator<ClassroomView> roomIter = possibleRooms
					.iterator(); roomIter.hasNext();) {

				final ClassroomView room = roomIter.next();
				if (room.getSchedule().isVacant(pair.getDaysInt(),
						pair.getTimeInt())) {
					roomAndSked.add(new RoomAndSkedPair(room, pair));
				}
			}

		}

		return roomAndSked;
	}

	/**
	 * The starting point of the approach or algorithm
	 */
	public void start() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SNA_courses_scheduled_" + datetime
				+ ".csv");

		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF COURSES SCHEDULED BY STUDENTS NEEDING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output
					.write("COURSE,% SCHEDULED,STUDENTS SCHEDULED,LEC SCHEDULED,LAB SCHEDULED,REMARKS");
			output.newLine();

			// initialize rooms
			this.initRoomsList();

			// initialize students
			this.initStudentsList();

			// initializes course types
			this.initCourseTypesList();

			System.out.println("Initialization Finished.\n");

			for (final Iterator<Integer> courseTypeIter = this.courseTypesList
					.iterator(); courseTypeIter.hasNext();) {
				final int courseType = courseTypeIter.next();

				// gets the courses with the specified courseType
				this.coursesList = CourseViewPeer.getCoursesWithType(
						courseType, this.coursesWithMissingInfo);

				// System.out.println(this.coursesList.size()
				// + " Courses with subjectType " + courseType);

				// sort subjects according to num of studentsNeeding
				CourseDemandUtility
						.sortCoursesByDescendingSize(this.coursesList);

				// iterates over all courses
				for (final Iterator<CourseView> courseIter = this.coursesList
						.iterator(); courseIter.hasNext();) {

					final CourseView course = courseIter.next();
					final String courseCode = course.getCourseCode();

					// continue if no students to process
					if (course.getRecommendedTo() < 1) {
						continue;
					}

					// get all the students needing the course
					final List<String> studentsNeeding = course
							.getStudentsNeeding();

					// gets the lecture information of the course
					final int lecDaysPerWeek = course
							.getLectureSessionsPerWeek();
					int maxNumOfLec = 0;
					final int maxNumOfLecStudents = this
							.getMaxLecStudents(course);
					final List<ClassroomView> possibleLecRooms = new ArrayList<ClassroomView>();
					final String lecRoomCategory = course
							.getLectureRoomCategory();
					final Schedule lecSkeds = new Schedule();
					int lecTimePerSession = 0;

					if (lecDaysPerWeek > 0) {
						lecTimePerSession = Math.round(course
								.getLectureHoursPerWeek()
								* 2 / lecDaysPerWeek);
					}

					// checks whether max number of sections to be scheduled is
					// specified or not
					if (course.getMaxNumberOfLectureSections() == -1) {
						maxNumOfLec = course.getPredictedNumOfLec();
					} else {
						maxNumOfLec = Math.min(course
								.getMaxNumberOfLectureSections(), course
								.getPredictedNumOfLec());

						// if (course.getPredictedNumOfLec() < course
						// .getMaxNumberOfLectureSections()) {
						// System.out.println("Less : "
						// + course.getCourseCode());
						// } else if (course.getPredictedNumOfLec() == course
						// .getMaxNumberOfLectureSections()) {
						// System.out.println("Equal : "
						// + course.getCourseCode());
						// }
					}

					// create lectures equal to the number of predicted number
					// of
					// lectures
					for (int j = 0; j < maxNumOfLec
							&& studentsNeeding.size() > 0; j++) {
						List<String> maxLecStudents = new ArrayList<String>();
						int maxFound = 0;
						RoomAndSkedPair maxLecRoomAndSked = null;
						boolean tbaLecSked = false;

						List<SchedulePair> possibleLecSkeds = lecSkeds
								.findVacant(lecDaysPerWeek, lecTimePerSession,
										courseCode,
										this.notYetImplementedCourses, course
												.getSubjectType());

						if (possibleLecSkeds == null) {
							possibleLecSkeds = new Schedule().findVacant(
									lecDaysPerWeek, lecTimePerSession,
									courseCode, this.notYetImplementedCourses,
									course.getSubjectType());
						}

						if (possibleLecSkeds != null) {
							final List<RoomAndSkedPair> lecRoomAndSkedList = this
									.getRoomAndSked(lecRoomCategory,
											possibleLecSkeds, possibleLecRooms,
											courseCode, maxNumOfLecStudents);

							// finds a schedule from the list of possible
							// lecture
							// schedules with the most number of students that
							// are
							// vacant
							for (final Iterator<RoomAndSkedPair> iter = lecRoomAndSkedList
									.iterator(); iter.hasNext();) {
								final RoomAndSkedPair roomAndSked = iter.next();

								// finds students who are vacant on the
								// specified
								// schedule
								final List<String> foundStudents = this
										.findStudentsVacantOn(studentsNeeding,
												roomAndSked.getSchedule(),
												maxNumOfLecStudents);
								final int foundStudentsSize = foundStudents
										.size();

								if (foundStudentsSize > maxFound) {
									maxLecStudents = foundStudents;
									maxFound = foundStudentsSize;
									maxLecRoomAndSked = roomAndSked;

									if (maxFound == maxNumOfLecStudents) {
										break;
									}
								}
							}

							// if no room and schedule that will fit the
							// students'
							// vacant time, schedule a TBA class room or a TBA
							// schedule
							if (maxLecRoomAndSked == null) {
								maxFound = 0;
								maxLecStudents = new ArrayList<String>();
								maxLecRoomAndSked = new RoomAndSkedPair();
								maxLecRoomAndSked.setRoom(null); // room is TBA

								// find a schedule with most number of students
								// that
								// are vacant
								for (final Iterator<SchedulePair> skedIter = possibleLecSkeds
										.iterator(); skedIter.hasNext();) {
									final SchedulePair lecSked = skedIter
											.next();
									final List<String> foundStudents = this
											.findStudentsVacantOn(
													studentsNeeding, lecSked,
													maxNumOfLecStudents);
									final int foundStudentsSize = foundStudents
											.size();

									if (foundStudentsSize > maxFound) {
										maxLecStudents = foundStudents;
										maxFound = foundStudentsSize;
										maxLecRoomAndSked.setSchedule(lecSked);

										if (maxFound == maxNumOfLecStudents) {
											break;
										}
									}
								}

								// case where both room and schedule is TBA
								if (maxLecRoomAndSked.getSchedule() == null) {
									tbaLecSked = true;
								}
							}
						} else {
							tbaLecSked = true;
						}

						if (tbaLecSked || maxLecStudents.size() < 1) {
							maxLecStudents = new ArrayList<String>();
							maxLecRoomAndSked = new RoomAndSkedPair();
							maxLecRoomAndSked.setRoom(null);
							maxLecRoomAndSked.setSchedule(null);
							int ctr = 0;

							if (maxNumOfLecStudents > 0) {
								for (final Iterator<String> studIter = studentsNeeding
										.iterator(); studIter.hasNext();) {
									maxLecStudents.add(studIter.next());
									ctr++;

									if (ctr == maxNumOfLecStudents) {
										break;
									}
								}
							} else {
								maxLecStudents.addAll(studentsNeeding);
							}
						}

						final SectionLecture lecture = new SectionLecture();
						final SchedulePair lecSked = maxLecRoomAndSked
								.getSchedule();
						final ClassroomView lecRoom = maxLecRoomAndSked
								.getRoom();

						lecture.setCourseCode(courseCode);

						if (lecSked != null) {
							lecture.setDaysInt(lecSked.getDaysInt());
							lecture.setTimeInt(lecSked.getTimeInt());
						} else {
							lecture.setDaysInt(null);
							lecture.setTimeInt(null);
						}

						if (lecRoom != null) {
							lecRoom.addSchedule(lecture.getDaysInt(), lecture
									.getTimeInt(), courseCode);
						}

						lecture.setRoom(lecRoom);
						lecture.setLecSectionName("NEW SECTION");

						// checks whether the subject has a laboratory class
						if (course.getHasLaboratory()) {
							final int labDaysPerWeek = course
									.getLaboratorySessionsPerWeek();
							final int maxNumOfLabStudents = course
									.getMaximumNumberOfLaboratoryStudents();
							final int maxNumOfLab = course.getMaxLabPerLec();
							final List<ClassroomView> possibleLabRooms = new ArrayList<ClassroomView>();
							final List<String> lectureStudents = maxLecStudents;
							final String labRoomCategory = course
									.getLaboratoryRoomCategory();
							final Schedule labSkeds = new Schedule();
							int labTimePerSession = 0;

							if (labDaysPerWeek > 0) {
								labTimePerSession = Math.round(course
										.getLaboratoryHoursPerWeek()
										* 2 / labDaysPerWeek);
							}

							if (lecSked != null) {
								labSkeds.addSchedule(lecture.getDaysInt(),
										lecture.getTimeInt(), lecture
												.getCourseCode());
							}

							for (int k = 0; k < maxNumOfLab
									&& lectureStudents.size() > 0; k++) {
								List<String> maxLabStudents = new ArrayList<String>();
								int maxLabStudentsFound = 0;
								RoomAndSkedPair maxLabRoomAndSked = null;
								boolean tbaLabSked = false;

								List<SchedulePair> possibleLabSkeds = labSkeds
										.findVacant(labDaysPerWeek,
												labTimePerSession, courseCode,
												this.notYetImplementedCourses,
												course.getSubjectType());

								if (possibleLabSkeds == null) {
									final Schedule temp = new Schedule();

									if (lecSked != null) {
										temp.addSchedule(lecture.getDaysInt(),
												lecture.getTimeInt(),
												courseCode);
									}

									possibleLabSkeds = temp.findVacant(
											labDaysPerWeek, labTimePerSession,
											courseCode,
											this.notYetImplementedCourses,
											course.getSubjectType());
								}

								if (possibleLabSkeds != null) {
									final List<RoomAndSkedPair> labRoomAndSkedList = this
											.getRoomAndSked(labRoomCategory,
													possibleLabSkeds,
													possibleLabRooms,
													courseCode,
													maxNumOfLabStudents);

									for (final Iterator<RoomAndSkedPair> iter = labRoomAndSkedList
											.iterator(); iter.hasNext();) {
										final RoomAndSkedPair labRoomAndSked = iter
												.next();
										final List<String> foundLabStudents = this
												.findStudentsVacantOn(
														lectureStudents,
														labRoomAndSked
																.getSchedule(),
														maxNumOfLabStudents);
										final int foundLabStudentsSize = foundLabStudents
												.size();

										if (foundLabStudentsSize > maxLabStudentsFound) {
											maxLabStudents = foundLabStudents;
											maxLabStudentsFound = foundLabStudentsSize;
											maxLabRoomAndSked = labRoomAndSked;

											if (maxLabStudentsFound == maxNumOfLabStudents) {
												break;
											}
										}
									}

									if (maxLabRoomAndSked == null) {
										maxLabStudentsFound = 0;
										maxLabStudents = new ArrayList<String>();
										maxLabRoomAndSked = new RoomAndSkedPair();
										maxLabRoomAndSked.setRoom(null); // room
										// is
										// TBA

										for (final Iterator<SchedulePair> skedIter = possibleLabSkeds
												.iterator(); skedIter.hasNext();) {
											final SchedulePair labSked = skedIter
													.next();
											final List<String> foundLabStudents = this
													.findStudentsVacantOn(
															lectureStudents,
															labSked,
															maxNumOfLabStudents);
											final int foundLabStudentsSize = foundLabStudents
													.size();

											if (foundLabStudentsSize > maxLabStudentsFound) {
												maxLabStudents = foundLabStudents;
												maxLabStudentsFound = foundLabStudentsSize;
												maxLabRoomAndSked
														.setSchedule(labSked);

												if (maxLabStudentsFound == maxNumOfLabStudents) {
													break;
												}
											}
										}

										if (maxLabRoomAndSked.getSchedule() == null) {
											tbaLabSked = true;
										}
									}
								} else {
									tbaLabSked = true;
								}

								// case where both lab room and schedule is
								// TBA
								if (tbaLabSked || maxLabStudents.size() < 1) {
									maxLabStudents = new ArrayList<String>();
									maxLabRoomAndSked = new RoomAndSkedPair();
									maxLabRoomAndSked.setRoom(null);
									maxLabRoomAndSked.setSchedule(null);
									int ctr = 0;

									if (maxNumOfLabStudents > 0) {
										for (final Iterator<String> studIter = lectureStudents
												.iterator(); studIter.hasNext();) {
											maxLabStudents.add(studIter.next());
											ctr++;

											if (ctr == maxNumOfLabStudents) {
												break;
											}
										}
									} else {
										maxLabStudents.addAll(lectureStudents);
									}
								}

								final SectionLaboratory laboratory = new SectionLaboratory();
								final SchedulePair labSked = maxLabRoomAndSked
										.getSchedule();
								final ClassroomView labRoom = maxLabRoomAndSked
										.getRoom();

								laboratory.setCourseCode(courseCode);

								if (labSked != null) {
									laboratory.setDaysInt(maxLabRoomAndSked
											.getSchedule().getDaysInt());
									laboratory.setTimeInt(maxLabRoomAndSked
											.getSchedule().getTimeInt());
								} else {
									laboratory.setDaysInt(null);
									laboratory.setTimeInt(null);
								}

								if (labRoom != null) {
									labRoom.addSchedule(
											laboratory.getDaysInt(), laboratory
													.getTimeInt(), courseCode);
								}

								laboratory.setRoom(labRoom);
								laboratory.setLabSectionName("LAB SECTION" + k);
								laboratory.setLecSectionName(lecture
										.getLecSectionName());

								for (final Iterator<String> studIter = maxLabStudents
										.iterator(); studIter.hasNext();) {

									final String studentNumber = studIter
											.next();
									final StudentView student = this.studentsList
											.get(studentNumber);
									boolean hasConflictLec = false;
									boolean hasConflictLab = false;

									if (lecSked != null) {
										hasConflictLec = student.addSchedule(
												lecture.getDaysInt(), lecture
														.getTimeInt(),
												courseCode);
									}

									if (labSked != null) {
										hasConflictLab = student.addSchedule(
												laboratory.getDaysInt(),
												laboratory.getTimeInt(),
												courseCode + " LAB");
									}

									if (!hasConflictLec && !hasConflictLab) {
										studentsNeeding.remove(studentNumber);
										lectureStudents.remove(studentNumber);
										laboratory.addStudent(student);
										lecture.addStudent(student);
									}
								}

								if (labRoom != null) {
									possibleLabRooms.add(laboratory.getRoom());
								}

								if (labSked != null) {
									labSkeds.addSchedule(laboratory
											.getDaysInt(), laboratory
											.getTimeInt(), laboratory
											.getLabSectionName());
								}

								lecture.addLabSection(laboratory);
								course.setNumOfLab(course.getNumOfLab() + 1);
							}
						} else {
							for (final Iterator<String> studIter = maxLecStudents
									.iterator(); studIter.hasNext();) {
								final String studentNumber = studIter.next();
								final StudentView student = this.studentsList
										.get(studentNumber);

								boolean hasConflictLec = false;

								if (lecSked != null) {
									hasConflictLec = student.addSchedule(
											lecture.getDaysInt(), lecture
													.getTimeInt(), courseCode);
								}

								if (!hasConflictLec) {
									studentsNeeding.remove(studentNumber);
									lecture.addStudent(student);
								}
							}
						}

						if (lecRoom != null) {
							possibleLecRooms.add(lecture.getRoom());
						}

						if (lecSked != null) {
							lecSkeds.addSchedule(lecture.getDaysInt(), lecture
									.getTimeInt(), lecture.getCourseCode());
						}

						course.addSection(lecture);
					}

					this.unscheduledStudentsMap.put(courseCode, studentsNeeding
							.size());

					// counts the underloaded students
					for (final Iterator<String> studIter = studentsNeeding
							.iterator(); studIter.hasNext();) {
						final String student = studIter.next();
						final StudentView studentObj = this.studentsList
								.get(student);
						studentObj.addUnscheduledCourse(courseCode);
						studentObj.addLackUnits(course.getCredit());

						if (this.underloadedStudentsMap.containsKey(student)) {
							this.underloadedStudentsMap
									.put(student, this.underloadedStudentsMap
											.get(student) + 1);
						} else {
							this.underloadedStudentsMap.put(student, 1);
						}
					}

				}

				this.writeToDatabase(output);

			}

			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		this.printSummary();

	}

	/**
	 * Writes the specified students with the specified sectionId to the
	 * database.
	 * 
	 * @param students
	 * @param sectionId
	 * @param size
	 */
	private void writeStudents(final List<StudentView> students,
			final int sectionId, final int size) {
		int ctr = 0;
		for (final Iterator<StudentView> studIter = students.iterator(); studIter
				.hasNext();) {
			final Slot slot = new Slot();
			slot.setSectionId(sectionId);
			try {
				slot.setStudentNumber(studIter.next().getStudentNumber());
			} catch (final Exception e) {
				continue;
			}
			slot.setStatus(SLOT_RESERVED);
			ctr++;

			Slot.add(slot);
		}

		for (int i = ctr; i < size; i++) {
			final Slot slot = new Slot();
			slot.setSectionId(sectionId);
			slot.setStatus(SLOT_VACANT);

			Slot.add(slot);
		}

	}

	/**
	 * Writes all the student slots and course sections information in the
	 * database.
	 * 
	 * @throws IOException
	 */
	private void writeToDatabase(final BufferedWriter output)
			throws IOException {
		// System.out.println("WRITING TO DB");
		for (final Iterator<CourseView> courseIter = this.coursesList
				.iterator(); courseIter.hasNext();) {
			final CourseView course = courseIter.next();
			final List<SectionLecture> lectureSections = course
					.getLectureSections();
			int scheduledStudents = 0;
			int sectionId;
			int maxStudents;
			int lecCtr = 1;
			String prevSection = "";
			CourseDemandUtility.sortLectureSections(lectureSections);
			final List<String> sameSections = this
					.assignSections(lectureSections);

			for (final Iterator<SectionLecture> lectIter = lectureSections
					.iterator(); lectIter.hasNext();) {
				final SectionLecture lecture = lectIter.next();
				final String lectSection = lecture.getLecSectionName();
				if (!lectSection.equals(prevSection)) {
					lecCtr = 1;
				}
				if (sameSections != null && sameSections.contains(lectSection)) {
					lecture.setLecSectionName(lectSection + lecCtr);
					lecCtr++;
				}

				prevSection = lectSection;

				lecture.setDays(ScheduleUtility.getDaysStringEquivalent(lecture
						.getDaysInt()));
				lecture.setTime(ScheduleUtility.getTimeStringEquivalent(lecture
						.getTimeInt()));
				scheduledStudents += lecture.size();

				if (lecture.size() == 0) {
					System.out.println("CHECK: " + lecture.getLecSectionName());
				}

				if (course.getHasLaboratory()) {
					int ctr = 1;
					final List<SectionLaboratory> labSections = lecture
							.getLabSections();
					CourseDemandUtility.sortLabSections(labSections);

					for (final Iterator<SectionLaboratory> labIter = labSections
							.iterator(); labIter.hasNext();) {
						final SectionLaboratory lab = labIter.next();
						final StringBuffer sb = new StringBuffer();
						lab.setLecSectionName(lecture.getLecSectionName());
						sb.append(lab.getLecSectionName());
						sb.append("-");
						sb.append(ctr);
						sb.append("L");
						lab.setLabSectionName(sb.toString());
						lab.setDays(ScheduleUtility.getDaysStringEquivalent(lab
								.getDaysInt()));
						lab.setTime(ScheduleUtility.getTimeStringEquivalent(lab
								.getTimeInt()));
						ctr++;

						maxStudents = course
								.getMaximumNumberOfLaboratoryStudents();

						if (maxStudents == 0) {
							maxStudents = lab.getStudents().size();
						}

						sectionId = SectionView.add(lecture, lab, maxStudents);
						this.writeStudents(lab.getStudents(), sectionId,
								maxStudents);
					}
				} else {
					maxStudents = course.getMaximumNumberOfLectureStudents();

					if (maxStudents == 0) {
						maxStudents = lecture.getStudents().size();
					}

					sectionId = SectionView.add(lecture, maxStudents);
					this.writeStudents(lecture.getStudents(), sectionId,
							maxStudents);
				}
			}

			course.setScheduledStudents(scheduledStudents);
			if (course.getRecommendedTo() != 0) {
				output.write(course.toStringStatistics());
				if (this.notYetImplementedCourses.contains(course
						.getCourseCode())) {
					output.write("-->NOT YET IMPLEMENTED");
				} else if (this.coursesWithMissingInfo.contains(course
						.getCourseCode())) {
					output.write("-->SUBJECT WITH MISSING INFO");
				}
				output.newLine();
			}
		}
	}
}
